#include <string.h>
#include <malloc.h>
#include <stdio.h>
#include "PacketQueue.h"

PacketQueue::PacketQueue() {
    pFirstElement_ = 0;
    pLastElement_ = 0;
    elementNum_ = 0;
    totalDataSize_ = 0;
    pMutex_ = SDL_CreateMutex();
    pCond_ = SDL_CreateCond();
}


PacketQueue::~PacketQueue() {
    Clear();

    SDL_DestroyMutex(pMutex_);
    SDL_DestroyCond(pCond_);
}


bool PacketQueue::Push(AVPacket *pPkt) {
	SDL_LockMutex(pMutex_);
	bool bRet = true;
	do 
	{
		if(pPkt == 0) {
			bRet = false;
			break;
		}

		if(av_dup_packet(pPkt) < 0) {
			bRet = false;
			break;
		}

		QUEUE_ELEMENT *elem = (QUEUE_ELEMENT *) malloc(sizeof(QUEUE_ELEMENT));
		if(!elem) {
			bRet = false;
			break;
		}

		elem->pkt = *pPkt;

		totalDataSize_ += pPkt->size;
		elementNum_++;

		// Add last element in queue.
		if(pLastElement_ == 0) {
			// Now,no element in queue.
			elem->prev = 0;
			elem->next = 0;
			pFirstElement_ = elem;
			pLastElement_ = elem;
		} else {
			elem->prev = pLastElement_;
			elem->next = 0;
			pLastElement_->next = elem;
			pLastElement_ = elem;
		}
	} while (false);
	
    SDL_UnlockMutex(pMutex_);
	SDL_CondSignal(pCond_);
    return bRet;
}


bool PacketQueue::Pop(AVPacket *pPkt, bool block) {
    bool bRet = false;

    SDL_LockMutex(pMutex_);
    do {
        if(elementNum_ == 0) {
            if(block) {
                SDL_CondWait(pCond_, pMutex_);
            } else {
                break;
            }
        } else {
            *pPkt = pFirstElement_->pkt;

            elementNum_--;
            totalDataSize_ -= pFirstElement_->pkt.size;

            QUEUE_ELEMENT *next = pFirstElement_->next;

            if(next != 0) {
                next->prev = 0;
                free(pFirstElement_);
                pFirstElement_ = next;
            } else {
                free(pFirstElement_);
                pFirstElement_ = 0;
                pLastElement_ = 0;
            }

            bRet = true;
            break;
        }
    } while (true);

    SDL_UnlockMutex(pMutex_);
    return bRet;
}


unsigned int PacketQueue::GetElementCount() {
    unsigned int rvalue;

    SDL_LockMutex(pMutex_);
    rvalue = elementNum_;
    SDL_UnlockMutex(pMutex_);

    return rvalue;
}


unsigned int PacketQueue::GetTotalDataSize() {
    unsigned int rvalue;

    SDL_LockMutex(pMutex_);
    rvalue = totalDataSize_;
    SDL_UnlockMutex(pMutex_);

    return rvalue;
}


unsigned int PacketQueue::Clear() {
    unsigned int rvalue;

    SDL_LockMutex(pMutex_);
    rvalue = elementNum_;

    if(elementNum_ > 0) { // free memory.
        QUEUE_ELEMENT *elem = pFirstElement_;
        QUEUE_ELEMENT *next = pFirstElement_;

        while(next) {
            next = next->next;
            free(elem);
            elem = next;
        }
    }

    pFirstElement_ = NULL;
    pLastElement_ = NULL;
    elementNum_ = 0;
    totalDataSize_ = 0;
    SDL_UnlockMutex(pMutex_);

    return rvalue;
}